from django_redis import get_redis_connection
import time
import json
import os
import subprocess
import uuid
from libs.utils import human_time, AttrDict
from application import settings
from .models import GameVersion


class ExecError(Exception):
    pass


def time_parser(start, stop):
    build_time = stop - start
    if int(build_time / 60) <= 0:
        return F"{build_time:.3f}秒"
    else:
        build_time_parser = str(build_time / 60).split('.')
        return F"{build_time_parser[0]}分{build_time_parser[1][0:1]}秒"


def deploy_log(script):
    ssh = script.host.get_ssh()
    # outputs = list()
    # for code, out in ssh.exec_command(script.log_command):
    #     outputs.append(out)
    # log_command = f'cat /tmp/nohup_test.log'
    code, outputs = ssh.exec_command(script.log_command)
    return code, outputs


def deploy_dispatch(env, task, token, script):
    start_time = time.time()
    rds = get_redis_connection()
    key = str(task.task_id) + '_' + str(task.build_id)
    test = 1
    try:
        api_token = uuid.uuid4().hex
        r_id = f'{task.task_id}:{task.build_id}'
        # rds.setex(api_token, 60 * 60, r_id)
        helper = Helper(rds, token, r_id)
        test = helper
        if task.state == 2:
            if not task.extend:
                _deploy_ext1_host(helper, task, env, script.command)
            else:
                _deploy_test(helper, task, env, script)
        task.state = 0
    except Exception as e:
        task.state = 1
        test.send_error(key, f'error error!!!!')
        raise e
    finally:
        task.build_time = time_parser(start_time, time.time())
        rds.expire(token, 5 * 60)
        rds.close()
        task.token = ''
        task.save()


# 普通脚本执行逻辑
def _deploy_ext1_host(helper, task, env, command):
    key = str(task.task_id) + '_' + str(task.build_id)
    helper.send_step(key, 0, 'info', f'\r\n{human_time()} 远程主机建立连接中...        ')
    with task.task.host.get_ssh() as ssh:
        helper.send_step(key, 1, 'info', f'\r\n{human_time()} 脚本执行中...        ')
        helper.remote(key, ssh, command, env)
    helper.send_step(key, 100, 'success', '执行完成\r\n')


# 更新测试服逻辑
def _deploy_test(helper, task, env, script):
    key = str(task.task_id) + '_' + str(task.build_id)
    helper.send_step(key, 0, 'info', f'\r\n{human_time()} 远程主机建立连接中...        ')
    with task.task.host.get_ssh() as ssh:
        helper.send_step(key, 1, 'info', f'\r\n{human_time()} 测试服更新中...        ')
        helper.remote(key, ssh, script.command, env)
    helper.send_step(key, 100, 'success', '更新完成\r\n')
    # 更新完成后将版本号写入版本表中
    params = AttrDict()
    for k, v in env.items():
        if k == 'Version':
            params.version = v
    params.game_type = task.extend
    params.language = script.language
    params.suffix = '-' + script.language + '.zip'
    GameVersion.objects.create(**params)


class Helper:
    def __init__(self, rds, token, r_id):
        self.rds = rds
        self.token = token
        self.log_key = f'{settings.REQUEST_KEY}:{r_id}'
        self.rds.delete(self.log_key)

    def _send(self, message):
        self.rds.lpush(self.token, json.dumps(message))
        self.rds.lpush(self.log_key, json.dumps(message))

    def send_info(self, key, message):
        self._send({'key': key, 'status': 'info', 'data': message})

    def send_error(self, key, message, with_break=True):
        message = '\r\n' + message
        self._send({'key': key, 'status': 'error', 'data': message})
        if with_break:
            raise ExecError

    def send_status(self, key, status, message):
        self._send({'key': key, 'status': status, 'data': message})

    def send_step(self, key, step, status, message):
        self._send({'key': key, 'step': step, 'status': status, 'data': message})

    def local(self, command, env=None):
        if env:
            env = dict(env.items())
            env.update(os.environ)
        command = 'set -e\n' + command
        task = subprocess.Popen(command, env=env, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        while True:
            message = task.stdout.readline()
            if not message:
                break
            self.send_info('local', message.decode())
        if task.wait() != 0:
            self.send_error('local', f'exit code: {task.returncode}')

    def remote(self, key, ssh, command, env=None):
        code = -1
        for code, out in ssh.exec_command_with_stream(command, environment=env):
            self.send_info(key, out)
        if code != 0:
            self.send_error(key, f'exit code: {code}')
